package com.liliudong.rommath.biz.sys.service.impl;


import cn.hutool.core.collection.CollUtil;
import com.liliudong.rommath.biz.sys.context.UserContext;
import com.liliudong.rommath.biz.sys.entity.SysRole;
import com.liliudong.rommath.biz.sys.mapper.SysRoleMapper;
import com.liliudong.rommath.biz.sys.param.SysRoleAddParam;
import com.liliudong.rommath.biz.sys.param.SysRoleModifyParam;
import com.liliudong.rommath.biz.sys.param.SysRolePageParam;
import com.liliudong.rommath.biz.sys.result.SysRolePageResult;
import com.liliudong.rommath.biz.sys.service.SysRoleMenuService;
import com.liliudong.rommath.biz.sys.service.SysRoleService;
import com.liliudong.rommath.biz.sys.service.SysUserRoleService;
import com.liliudong.rommath.common.exception.BizException;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static com.liliudong.rommath.biz.sys.entity.table.SysRoleTableDef.SYS_ROLE;
import static com.liliudong.rommath.biz.sys.entity.table.SysUserRoleTableDef.SYS_USER_ROLE;

/**
 * 系统角色 服务层实现。
 *
 * @author liudong.li
 * @since 1.0.0
 */
@Service
@RequiredArgsConstructor
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    private final SysUserRoleService sysUserRoleService;

    private final SysRoleMenuService sysRoleMenuService;

    @Override
    public List<String> selectCodesByUserIdIgnoreDataPermission(Long userId) {
        boolean superAdmin = UserContext.isSuperAdmin(userId);
        if (superAdmin) {
            return queryChain().select(SYS_ROLE.CODE).listAs(String.class);
        }
        return queryChainIgnoreDataPermission()
                .select(SYS_ROLE.CODE)
                .innerJoin(SYS_USER_ROLE).on(SYS_USER_ROLE.ROLE_ID.eq(SYS_ROLE.ID))
                .where(
                        SYS_USER_ROLE.USER_ID.eq(userId)
                )
                .listAs(String.class);
    }

    @Override
    public List<Integer> selectDataScopesByUserIdIgnoreDataPermission(Long userId) {
        boolean superAdmin = UserContext.isSuperAdmin(userId);
        if (superAdmin) {
            return Collections.singletonList(SysRole.DataScope.ALL.getCode());
        }
        return queryChainIgnoreDataPermission()
                .select(SYS_ROLE.DATA_SCOPE)
                .innerJoin(SYS_USER_ROLE).on(SYS_USER_ROLE.ROLE_ID.eq(SYS_ROLE.ID))
                .where(
                        SYS_USER_ROLE.USER_ID.eq(userId)
                )
                .listAs(Integer.class);
    }

    @Override
    @Transactional
    public void add(SysRoleAddParam param) {
        SysRole sysRole = SysRole.of(param);
        addOrModifyCheck(sysRole);
        save(sysRole);
        // 保存角色菜单关联
        List<Long> menuIdList = param.getMenuIdList();
        if (CollUtil.isNotEmpty(menuIdList)) {
            // 前端传过来的都是叶子节点，需要递归查询父节点
            Set<Long> parentMenuIdList = new HashSet<>(menuIdList);
            while (true) {
                List<Long> topParentMenuIdList = sysRoleMenuService.getParentMenuIdList(menuIdList);
                if (CollUtil.isEmpty(topParentMenuIdList)) {
                    break;
                }
                parentMenuIdList.addAll(topParentMenuIdList);
            }
            sysRoleMenuService.saveBatchByRoleId(sysRole.getId(), parentMenuIdList);
        }
    }

    @Override
    @Transactional
    public void deleteById(Long id) {
        // 角色被用户关联不能删除
        boolean exists = sysUserRoleService.existsByRoleId(id);
        if (exists) {
            throw new BizException("角色被用户关联不能删除");
        }
        // 删除角色
        removeById(id);
        // 删除角色菜单关联
        sysRoleMenuService.removeByRoleId(id);
    }

    @Override
    public void modify(SysRoleModifyParam param) {
        SysRole sysRole = SysRole.of(param);
        addOrModifyCheck(sysRole);
        updateById(sysRole);
    }

    @Override
    public Page<SysRolePageResult> queryPage(SysRolePageParam param) {
        String name = param.getName();
        String code = param.getCode();
        Long page = param.getPage();
        Long pageSize = param.getPageSize();

        return queryChain()
                .select(
                        SYS_ROLE.NAME,
                        SYS_ROLE.DATA_SCOPE,
                        SYS_ROLE.CODE,
                        SYS_ROLE.DESCRIPTION,
                        SYS_ROLE.CREATED_AT,
                        SYS_ROLE.UPDATED_AT
                )
                .where(SYS_ROLE.NAME.like(name).and(SYS_ROLE.CODE.like(code)))
                .pageAs(Page.of(page, pageSize), SysRolePageResult.class);
    }

    /**
     * 添加或修改检查
     *
     * @param sysRole 系统角色
     */
    private void addOrModifyCheck(SysRole sysRole) {
        String name = sysRole.getName();
        String code = sysRole.getCode();
        // 角色名称不能重复
        boolean existsName = queryChain()
                .where(
                        SYS_ROLE.NAME.eq(name)
                                .and(SYS_ROLE.ID.ne(sysRole.getId(), Objects.nonNull(sysRole.getId())))
                )
                .exists();
        if (existsName) {
            throw new BizException("角色名称已存在");
        }
        // 角色编码不能重复
        boolean existsCode = queryChain()
                .where(
                        SYS_ROLE.CODE.eq(code)
                                .and(SYS_ROLE.ID.ne(sysRole.getId(), Objects.nonNull(sysRole.getId())))
                )
                .exists();
        if (existsCode) {
            throw new BizException("角色编码已存在");
        }
    }
}